package com.ikingtech.platform.service.system.variable.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ikingtech.framework.sdk.base.model.PageResult;
import com.ikingtech.framework.sdk.cache.constants.CacheConstants;
import com.ikingtech.framework.sdk.context.event.SystemInitEvent;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.system.variable.VariableEnum;
import com.ikingtech.framework.sdk.enums.system.variable.VariableTypeEnum;
import com.ikingtech.framework.sdk.log.embedded.annotation.OperationLog;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.variable.api.VariableApi;
import com.ikingtech.framework.sdk.variable.model.VariableDTO;
import com.ikingtech.framework.sdk.variable.model.VariableQueryParamDTO;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.system.variable.entity.VariableDO;
import com.ikingtech.platform.service.system.variable.exception.VariableExceptionInfo;
import com.ikingtech.platform.service.system.variable.service.VariableRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
@ApiController(value = "/system/variable", name = "系统管理-系统参数管理", description = "系统管理-系统参数管理")
public class VariableController implements VariableApi {

    private final VariableRepository repo;

    private final StringRedisTemplate redisTemplate;

    @Override
    @OperationLog(value = "新增系统参数", dataId = "#_res.getData()")
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(VariableDTO variable) {
        if (this.repo.exists(Wrappers.<VariableDO>lambdaQuery().eq(VariableDO::getName, variable.getName()))) {
            throw new FrameworkException(VariableExceptionInfo.DUPLICATE_VARIABLE_NAME);
        }
        if (this.repo.exists(Wrappers.<VariableDO>lambdaQuery().eq(VariableDO::getVariableKey, variable.getVariableKey()))) {
            throw new FrameworkException(VariableExceptionInfo.DUPLICATE_VARIABLE_KEY);
        }
        // 将DTO对象转换为DO对象
        VariableDO entity = Tools.Bean.copy(variable, VariableDO.class);
        // 生成唯一ID
        entity.setId(Tools.Id.uuid());
        // 设置租户代码
        // 保存实体到数据库
        this.repo.save(entity);
        // 将实体同步到缓存
        this.syncVariableToCache(entity);
        // 返回添加结果
        return R.ok(entity.getId());
    }

    @Override
    @OperationLog(value = "删除系统参数")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        String variableKey = this.repo.getObj(Wrappers.<VariableDO>lambdaQuery().select(VariableDO::getVariableKey).eq(VariableDO::getId, id), String.class::cast);
        // 删除指定id的数据
        this.repo.removeById(id);
        this.redisTemplate.opsForHash().delete(CacheConstants.SYSTEM_VARIABLE, variableKey);
        return R.ok();
    }

    @Override
    @OperationLog(value = "更新系统参数")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(VariableDTO variable) {
        // 根据变量ID获取原始实体
        VariableDO originEntity = this.repo.getById(variable.getId());
        if (originEntity == null) {
            throw new FrameworkException(VariableExceptionInfo.VARIABLE_NOT_FOUND);
        }
        if (this.repo.exists(Wrappers.<VariableDO>lambdaQuery()
                .ne(VariableDO::getId, variable.getId())
                .eq(VariableDO::getName, variable.getName()))) {
            throw new FrameworkException(VariableExceptionInfo.DUPLICATE_VARIABLE_NAME);
        }
        if (this.repo.exists(Wrappers.<VariableDO>lambdaQuery()
                .ne(VariableDO::getId, variable.getId())
                .eq(VariableDO::getVariableKey, variable.getVariableKey()))) {
            throw new FrameworkException(VariableExceptionInfo.DUPLICATE_VARIABLE_KEY);
        }
        // 如果变量类型为系统类型且变量值不合法，则抛出异常
        if (VariableTypeEnum.SYSTEM.name().equals(originEntity.getType()) &&
                !VariableEnum.validate(variable.getVariableKey(), variable.getVariableValue())) {
            throw new FrameworkException(VariableExceptionInfo.INVALID_VARIABLE_VALUE);
        }
        // 复制变量DTO对象到VariableDO对象，并设置租户代码
        VariableDO entity = Tools.Bean.copy(variable, VariableDO.class);
        // 更新数据库中的变量
        this.repo.updateById(entity);
        // 同步变量到缓存
        this.syncVariableToCache(entity);
        return R.ok();
    }

    @Override
    public R<List<VariableDTO>> page(VariableQueryParamDTO queryParam) {
        return R.ok(PageResult.build(this.repo.page(new Page<>(queryParam.getPage(), queryParam.getRows()), VariableRepository.createWrapper(queryParam, Me.tenantCode()))).convert(entity -> {
            VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
            if (null != variable.getType()) {
                variable.setTypeName(variable.getType().description);
            }
            return variable;
        }));
    }

    @Override
    public R<List<VariableDTO>> all() {
        return R.ok(Tools.Coll.convertList(this.repo.list(Wrappers.<VariableDO>query().lambda().orderByDesc(VariableDO::getCreateTime)), entity -> {
            VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
            if (null != variable.getType()) {
                variable.setTypeName(variable.getType().description);
            }
            return variable;
        }));
    }

    @Override
    public R<VariableDTO> detail(String id) {
        // 通过ID获取变量实体
        VariableDO entity = this.repo.getById(id);
        if (null == entity) {
            throw new FrameworkException(VariableExceptionInfo.VARIABLE_NOT_FOUND);
        }
        // 将变量实体转换为变量DTO对象
        VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
        if (null != variable.getType()) {
            // 设置变量类型名称
            variable.setTypeName(variable.getType().description);
        }
        // 返回变量详情
        return R.ok(variable);
    }

    @Override
    public R<String> getValueByKey(String key) {
        return R.ok(this.repo.getObj(Wrappers.<VariableDO>lambdaQuery()
                .select(VariableDO::getVariableValue)
                .eq(VariableDO::getVariableKey, key), String.class::cast));
    }

    @EventListener(SystemInitEvent.class)
    public void systemInitEventListener() {
        List<String> existVariableKeys = this.repo.listObjs(Wrappers.<VariableDO>lambdaQuery()
                .select(VariableDO::getVariableKey)
                .in(VariableDO::getVariableKey, Tools.Array.convertList(VariableEnum.values(), VariableEnum::name)));
        List<VariableDO> entities = Tools.Array.convertList(VariableEnum.values(),
                variable -> !existVariableKeys.contains(variable.name()), variable -> {
                    VariableDO variableDO = new VariableDO();
                    variableDO.setId(Tools.Id.uuid());
                    variableDO.setVariableKey(variable.name());
                    variableDO.setVariableValue(variable.value);
                    variableDO.setType(variable.type.name());
                    variableDO.setName(variable.description);
                    return variableDO;
                });
        if (Tools.Coll.isNotBlank(entities)) {
            this.repo.saveBatch(entities);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Scheduled(fixedDelay = 5 * 60, initialDelay = 60, timeUnit = TimeUnit.SECONDS)
    public void syncVariableCache() {
        List<VariableDO> entities = this.repo.list();
        if (Tools.Coll.isBlank(entities)) {
            return;
        }
        this.redisTemplate.opsForHash().putAll(CacheConstants.SYSTEM_VARIABLE,
                Tools.Coll.convertMap(entities, VariableDO::getVariableKey, VariableDO::getVariableValue));
    }

    private void syncVariableToCache(VariableDO entity) {
        if (null == entity) {
            return;
        }
        this.redisTemplate.opsForHash().put(CacheConstants.SYSTEM_VARIABLE, entity.getVariableKey(), entity.getVariableValue());
    }
}
